package sis.studentinfo;

import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.logging.*;

/**
 * This class provides a representation of a single-semester
 * session of a specific university course.
 * @author Administrator
 */
abstract public class Session implements Comparable<Session>, Iterable<Student> {

    protected static int count;
    final static String CompareToNullErrorMsg = "Can't compare session object to null";
    final static String LessThanZeroCreditsErrorMsg = "Number of Credits cannot be less than 0";
    final static String StudentAlreadyEnrolledErrorMsg = "Student is already enrolled in this course";
    final static Logger logger = Logger.getLogger(Session.class.getName());
    protected String department;
    protected String number;
    private List<Student> students = new ArrayList<Student>();
    private Date startDate;
    private int numberOfCredits;

    /**
     * Constructs a CourseSession starting on a specific date
     * @param startDate the date on which the CourseSession begins
     */
    protected Session(
            String department, String number, Date startDate) {
        ValidateUtil.checkNullEmpty(new SimpleEntry("department", department),
                new SimpleEntry("number", number),
                new SimpleEntry("startDate", startDate));
        this.department = department;
        this.number = number;
        this.startDate = startDate;
        Session.incrementCount();
    }

    static private void incrementCount() {
        ++count;
    }

    static void resetCount() {
        count = 0;
    }

    static int getCount() {
        return count;
    }

    @Override
    public int compareTo(Session that) {
        if (that == null) {
            logger.info(CompareToNullErrorMsg);
            throw new ClassCastException(CompareToNullErrorMsg);
        }
        int compare =
                this.getDepartment().compareTo(that.getDepartment());
        if (compare != 0) {
            return compare;
        }
        compare = this.getNumber().compareTo(that.getNumber());
        if (compare != 0) {
            return compare;
        }
        return this.getStartDate().compareTo(that.getStartDate());
    }

    void setNumberOfCredits(int numberOfCredits) {
        if (numberOfCredits < 0) {
            logger.info(LessThanZeroCreditsErrorMsg);
            throw new IllegalArgumentException(LessThanZeroCreditsErrorMsg);
        }
        this.numberOfCredits = numberOfCredits;
    }

    public String getDepartment() {
        return department;
    }

    public String getNumber() {
        return number;
    }

    int getNumberOfStudents() {
        return students.size();
    }

    public void enroll(Student student) {
        ValidateUtil.checkNullEmpty(new SimpleEntry("student", student));
        if (students.contains(student)) {
            logger.info(StudentAlreadyEnrolledErrorMsg);
            throw new IllegalArgumentException(StudentAlreadyEnrolledErrorMsg);
        }
        student.addCredits(numberOfCredits);
        students.add(student);
    }

    Student get(int index) {
        return students.get(index);
    }

    Student getCopy(int index) {
        return new Student(students.get(index));
    }

    Date getStartDate() {
        return startDate;
    }

    /** returns an unmodifiable ArrayList of students */
    public List<Student> getAllStudents() {
        return Collections.unmodifiableList(students);
    }

    /**
     * @return Date the last date of the course session
     */
    Date getEndDate() {
        GregorianCalendar calendar = new GregorianCalendar();
        calendar.setTime(startDate);
        final int sessionLength = getSessionLength();
        final int daysInWeek = 7;
        final int daysFromFridayToMonday = 3;
        int numberOfDays =
                sessionLength * daysInWeek - daysFromFridayToMonday;
        calendar.add(Calendar.DAY_OF_YEAR, numberOfDays);
        return calendar.getTime();
    }

    double averageGpaForPartTimeStudents() {
        double total = 0.0;
        int count1 = 0;
        for (Student student : students) {
            if (student.isFullTime()) {
                continue;
            }
            count1++;
            total += student.getGpa();
        }
        if (count1 == 0) {
            return 0.0;
        }
        return total / count1;
    }

    @Override
    public Iterator<Student> iterator() {
        return students.iterator();
    }

    abstract int getSessionLength();
}